//
//  UIView+CWToast.m
//  CWDevelop
//
//  Created by LittoCats on 7/26/14.
//  Copyright (c) 2014 Littocats. All rights reserved.
//

#import "UIView+CWToast.h"
#import <QuartzCore/QuartzCore.h>
#import <objc/runtime.h>
#import <objc/message.h>

// general appearance
static const CGFloat kCWToastMaxWidth            = 0.8;      // 80% of parent view width
static const CGFloat kCWToastMaxHeight           = 0.8;      // 80% of parent view height
static const CGFloat kCWToastHorizontalPadding   = 10.0;
static const CGFloat kCWToastVerticalPadding     = 10.0;
static const CGFloat kCWToastCornerRadius        = 10.0;
static const CGFloat kCWToastOpacity             = 0.8;
static const CGFloat kCWToastFontSize            = 16.0;
static const CGFloat kCWToastMaxTitleLines       = 0;
static const CGFloat kCWToastMaxMessageLines     = 0;
static const CGFloat kCWToastFadeDuration        = 0.2;

// shadow appearance
static const CGFloat kCWToastShadowOpacity       = 0.8;
static const CGFloat kCWToastShadowRadius        = 6.0;
static const CGSize  kCWToastShadowOffset        = { 4.0, 4.0 };
static const BOOL    kCWToastDisplayShadow       = YES;

// display duration and position
static const CGFloat kCWToastDefaultDuration     = 3.0;
static const NSString * kCWToastDefaultPosition  = @"bottom";

// image view size
static const CGFloat kCWToastImageViewWidth      = 80.0;
static const CGFloat kCWToastImageViewHeight     = 80.0;

// activity
static const CGFloat kCWToastActivityWidth       = 80.0;
static const CGFloat kCWToastActivityHeight      = 80.0;
static const NSString * kCWToastActivityDefaultPosition = @"center";
static const NSString * kCWToastActivityViewKey  = @"CSToastActivityViewKey";
static const NSString * kCWToastActivityCountKey = @"kCWToastActivityCountKey";

@interface UIView (CWToastPrivate)

- (CGPoint)centerPointForPosition:(id)position withToast:(UIView *)toast;
- (UIView *)viewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image;

@end

@implementation UIView (CWToast)

#pragma mark - Toast Methods

- (void)makeToast:(NSString *)message {
    [self makeToast:message duration:kCWToastDefaultDuration position:kCWToastDefaultPosition];
}

- (void)makeToast:(NSString *)message duration:(CGFloat)interval position:(id)position {
    UIView *toast = [self viewForMessage:message title:nil image:nil];
    [self showToast:toast duration:interval position:position];
}

- (void)makeToast:(NSString *)message duration:(CGFloat)interval position:(id)position title:(NSString *)title {
    UIView *toast = [self viewForMessage:message title:title image:nil];
    [self showToast:toast duration:interval position:position];
}

- (void)makeToast:(NSString *)message duration:(CGFloat)interval position:(id)position image:(UIImage *)image {
    UIView *toast = [self viewForMessage:message title:nil image:image];
    [self showToast:toast duration:interval position:position];
}

- (void)makeToast:(NSString *)message duration:(CGFloat)interval  position:(id)position title:(NSString *)title image:(UIImage *)image {
    UIView *toast = [self viewForMessage:message title:title image:image];
    [self showToast:toast duration:interval position:position];
}

- (void)showToast:(UIView *)toast {
    [self showToast:toast duration:kCWToastDefaultDuration position:kCWToastDefaultPosition];
}

- (void)showToast:(UIView *)toast duration:(CGFloat)interval position:(id)point {
    toast.center = [self centerPointForPosition:point withToast:toast];
    toast.alpha = 0.0;
    
    SEL addOverlay = NSSelectorFromString(@"addOverlay:");
    if ([self respondsToSelector:addOverlay]) {
        objc_msgSend(self, addOverlay, toast);
    }else{
        [self addSubview:toast];
    }
    
    
    [UIView animateWithDuration:kCWToastFadeDuration
                          delay:0.0
                        options:UIViewAnimationOptionCurveEaseOut
                     animations:^{
                         toast.alpha = 1.0;
                     } completion:^(BOOL finished) {
                         [UIView animateWithDuration:kCWToastFadeDuration
                                               delay:interval
                                             options:UIViewAnimationOptionCurveEaseIn
                                          animations:^{
                                              toast.alpha = 0.0;
                                          } completion:^(BOOL finished) {
                                              [toast removeFromSuperview];
                                          }];
                     }];
}

#pragma mark - Toast Activity Methods

- (void)makeToastActivity {
    [self makeToastActivity:kCWToastActivityDefaultPosition withMessage:nil];
}

- (void)makeToastActivityWithMessage:(NSString *)message
{
    [self makeToastActivity:kCWToastActivityDefaultPosition withMessage:message];
}

- (void)makeToastActivity:(id)position withMessage:(NSString *)message
{
    NSInteger count = [self activityCount];
    if (count) {
        [self setActivityCount:count+1];
        return;
    }
    
    UIView *activityView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kCWToastActivityWidth, kCWToastActivityHeight)] ;
    activityView.center = [self centerPointForPosition:position withToast:activityView];
    activityView.backgroundColor = [[UIColor clearColor] colorWithAlphaComponent:kCWToastOpacity];
    activityView.alpha = 1.0;
    activityView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin);
    activityView.layer.cornerRadius = kCWToastCornerRadius;
    
    if (kCWToastDisplayShadow) {
        activityView.layer.shadowColor = [UIColor blackColor].CGColor;
        activityView.layer.shadowOpacity = kCWToastShadowOpacity;
        activityView.layer.shadowRadius = kCWToastShadowRadius;
        activityView.layer.shadowOffset = kCWToastShadowOffset;
    }
    
    
    UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
    activityIndicator.center = CGPointMake(activityView.bounds.size.width / 2, activityView.bounds.size.height / (message ? 3 : 2));
    [activityView addSubview:activityIndicator];
    [activityIndicator setBackgroundColor:[[UIColor clearColor] colorWithAlphaComponent:kCWToastOpacity]];
    [activityIndicator startAnimating];
    
    if (message) {
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, kCWToastActivityWidth, kCWToastActivityHeight / 3)];
        [label setCenter:CGPointMake(kCWToastActivityWidth / 2, kCWToastActivityHeight / 3* 2)];
        [label setBackgroundColor:[UIColor clearColor]];
        [label setFont:[UIFont systemFontOfSize:13]];
        [label setTextColor:[UIColor whiteColor]];
        [label setTextAlignment:NSTextAlignmentCenter];
        [activityView addSubview:label];
        label.text = message;
    }
    
    UIView *activityOverlay = [[UIView alloc] initWithFrame:self.frame];
    activityOverlay.alpha = 0.0;
    [activityOverlay setBackgroundColor:[[UIColor clearColor] colorWithAlphaComponent:0.3]];
    [activityOverlay addSubview:activityView];
    // associate ourselves with the activity view
    objc_setAssociatedObject (self, &kCWToastActivityViewKey, activityOverlay, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
////    [self addSubview:activityOverlay];
//    [self.layer addSublayer:activityOverlay.layer];
    
    SEL addOverlay = NSSelectorFromString(@"addOverlay:");
    if ([self respondsToSelector:addOverlay]) {
        objc_msgSend(self, addOverlay, activityOverlay);
    }else{
        [self addSubview:activityOverlay];
    }
    
    [self setActivityCount:1];
    
    [UIView animateWithDuration:kCWToastFadeDuration
                          delay:0.0
                        options:UIViewAnimationOptionCurveEaseOut
                     animations:^{
                         activityOverlay.alpha = 1.0;
                     } completion:nil];
}

- (void)hideToastActivity {
    NSInteger count = [self activityCount] - 1;
    if (count) {
        [self setActivityCount:count];
        return;
    }
    UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &kCWToastActivityViewKey);
    if (existingActivityView != nil) {
        [UIView animateWithDuration:kCWToastFadeDuration
                              delay:0.0
                            options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState)
                         animations:^{
                             existingActivityView.alpha = 0.0;
                         } completion:^(BOOL finished) {
                             [existingActivityView removeFromSuperview];
                             objc_setAssociatedObject (self, &kCWToastActivityViewKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
                         }];
    }
}

#pragma mark- activity count
- (void)setActivityCount:(NSInteger)count
{
    objc_setAssociatedObject(self, &kCWToastActivityCountKey, @(count), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSInteger)activityCount
{
    return [objc_getAssociatedObject(self, &kCWToastActivityCountKey) integerValue];
}

#pragma mark - Private Methods

- (CGPoint)centerPointForPosition:(id)point withToast:(UIView *)toast {
    if([point isKindOfClass:[NSString class]]) {
        // convert string literals @"top", @"bottom", @"center", or any point wrapped in an NSValue object into a CGPoint
        if([point caseInsensitiveCompare:@"top"] == NSOrderedSame) {
            return CGPointMake(self.bounds.size.width/2, (toast.frame.size.height / 2) + kCWToastVerticalPadding);
        } else if([point caseInsensitiveCompare:@"bottom"] == NSOrderedSame) {
            return CGPointMake(self.bounds.size.width/2, (self.bounds.size.height - (toast.frame.size.height / 2)) - kCWToastVerticalPadding);
        } else if([point caseInsensitiveCompare:@"center"] == NSOrderedSame) {
            return CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
        }
    } else if ([point isKindOfClass:[NSValue class]]) {
        return [point CGPointValue];
    }
    
    NSLog(@"Warning: Invalid position for toast.");
    return [self centerPointForPosition:kCWToastDefaultPosition withToast:toast];
}

- (UIView *)viewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image {
    // sanity
    if((message == nil) && (title == nil) && (image == nil)) return nil;
    
    // dynamically build a toast view with any combination of message, title, & image.
    UILabel *messageLabel = nil;
    UILabel *titleLabel = nil;
    UIImageView *imageView = nil;
    
    // create the parent view
    UIView *wrapperView = [[UIView alloc] init];
    wrapperView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin);
    wrapperView.layer.cornerRadius = kCWToastCornerRadius;
    
    if (kCWToastDisplayShadow) {
        wrapperView.layer.shadowColor = [UIColor blackColor].CGColor;
        wrapperView.layer.shadowOpacity = kCWToastShadowOpacity;
        wrapperView.layer.shadowRadius = kCWToastShadowRadius;
        wrapperView.layer.shadowOffset = kCWToastShadowOffset;
    }
    
    wrapperView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:kCWToastOpacity];
    
    if(image != nil) {
        imageView = [[UIImageView alloc] initWithImage:image];
        imageView.contentMode = UIViewContentModeScaleAspectFit;
        imageView.frame = CGRectMake(kCWToastHorizontalPadding, kCWToastVerticalPadding, kCWToastImageViewWidth, kCWToastImageViewHeight);
    }
    
    CGFloat imageWidth, imageHeight, imageLeft;
    
    // the imageView frame values will be used to size & position the other views
    if(imageView != nil) {
        imageWidth = imageView.bounds.size.width;
        imageHeight = imageView.bounds.size.height;
        imageLeft = kCWToastHorizontalPadding;
    } else {
        imageWidth = imageHeight = imageLeft = 0.0;
    }
    
    if (title != nil) {
        titleLabel = [[UILabel alloc] init];
        titleLabel.numberOfLines = kCWToastMaxTitleLines;
        titleLabel.font = [UIFont boldSystemFontOfSize:kCWToastFontSize];
        titleLabel.textAlignment = NSTextAlignmentLeft;
        titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
        titleLabel.textColor = [UIColor whiteColor];
        titleLabel.backgroundColor = [UIColor clearColor];
        titleLabel.alpha = 1.0;
        titleLabel.text = title;
        
        // size the title label according to the length of the text
        CGSize maxSizeTitle = CGSizeMake((self.bounds.size.width * kCWToastMaxWidth) - imageWidth, self.bounds.size.height * kCWToastMaxHeight);
        CGSize expectedSizeTitle = [title sizeWithFont:titleLabel.font constrainedToSize:maxSizeTitle lineBreakMode:titleLabel.lineBreakMode];
        titleLabel.frame = CGRectMake(0.0, 0.0, expectedSizeTitle.width, expectedSizeTitle.height);
    }
    
    if (message != nil) {
        messageLabel = [[UILabel alloc] init];
        messageLabel.numberOfLines = kCWToastMaxMessageLines;
        messageLabel.font = [UIFont systemFontOfSize:kCWToastFontSize];
        messageLabel.lineBreakMode = NSLineBreakByWordWrapping;
        messageLabel.textColor = [UIColor whiteColor];
        messageLabel.backgroundColor = [UIColor clearColor];
        messageLabel.alpha = 1.0;
        messageLabel.text = message;
        
        // size the message label according to the length of the text
        CGSize maxSizeMessage = CGSizeMake((self.bounds.size.width * kCWToastMaxWidth) - imageWidth, self.bounds.size.height * kCWToastMaxHeight);
        CGSize expectedSizeMessage = [message sizeWithFont:messageLabel.font constrainedToSize:maxSizeMessage lineBreakMode:messageLabel.lineBreakMode];
        messageLabel.frame = CGRectMake(0.0, 0.0, expectedSizeMessage.width, expectedSizeMessage.height);
    }
    
    // titleLabel frame values
    CGFloat titleWidth, titleHeight, titleTop, titleLeft;
    
    if(titleLabel != nil) {
        titleWidth = titleLabel.bounds.size.width;
        titleHeight = titleLabel.bounds.size.height;
        titleTop = kCWToastVerticalPadding;
        titleLeft = imageLeft + imageWidth + kCWToastHorizontalPadding;
    } else {
        titleWidth = titleHeight = titleTop = titleLeft = 0.0;
    }
    
    // messageLabel frame values
    CGFloat messageWidth, messageHeight, messageLeft, messageTop;
    
    if(messageLabel != nil) {
        messageWidth = messageLabel.bounds.size.width;
        messageHeight = messageLabel.bounds.size.height;
        messageLeft = imageLeft + imageWidth + kCWToastHorizontalPadding;
        messageTop = titleTop + titleHeight + kCWToastVerticalPadding;
    } else {
        messageWidth = messageHeight = messageLeft = messageTop = 0.0;
    }
    
    
    CGFloat longerWidth = MAX(titleWidth, messageWidth);
    CGFloat longerLeft = MAX(titleLeft, messageLeft);
    
    // wrapper width uses the longerWidth or the image width, whatever is larger. same logic applies to the wrapper height
    CGFloat wrapperWidth = MAX((imageWidth + (kCWToastHorizontalPadding * 2)), (longerLeft + longerWidth + kCWToastHorizontalPadding));
    CGFloat wrapperHeight = MAX((messageTop + messageHeight + kCWToastVerticalPadding), (imageHeight + (kCWToastVerticalPadding * 2)));
    
    wrapperView.frame = CGRectMake(0.0, 0.0, wrapperWidth, wrapperHeight);
    
    if(titleLabel != nil) {
        titleLabel.frame = CGRectMake(titleLeft, titleTop, titleWidth, titleHeight);
        [wrapperView addSubview:titleLabel];
    }
    
    if(messageLabel != nil) {
        messageLabel.frame = CGRectMake(messageLeft, messageTop, messageWidth, messageHeight);
        [wrapperView addSubview:messageLabel];
    }
    
    if(imageView != nil) {
        [wrapperView addSubview:imageView];
    }
    
    return wrapperView;
}

@end
